import { Command } from "commander";
import { Log, LogStatus, LogType } from "../../util/Logger";
import { BridgeConfig } from "../core/BridgeConfig";
import * as fs from "fs";
import { BridgeDirection, createProviders, initProviders } from "../core/ChainProviderFactory";

export type StressArgs = {
    direction: BridgeDirection;
    bridgeConfig: BridgeConfig;
    amount: number;
};

const parse = (args: any): StressArgs | undefined => {
    const validNumber = (n: any): boolean => !isNaN(Number(n)) && Number(n) >= 0;
    if (args.origin && (args.origin == "xrp" || args.origin == "evm") && args.config && validNumber(args.amount)) {
        return {
            direction: args.origin === "xrp" ? BridgeDirection.XRP_TO_EVM : BridgeDirection.EVM_TO_XRP,
            bridgeConfig: new BridgeConfig(JSON.parse(fs.readFileSync(args.config).toString())),
            amount: Number(args.amount),
        };
    }
    return undefined;
};

const program = new Command("claim")
    .option("-o, --origin <xrp|evm>", "Origin chain to execute the claim")
    .option("-op, --origin-private-key <value>", "Origin private key")
    .option("-dp, --destination-private-key <value>", "Destination private key")
    .option("-c, --config <value>", "Exported bridge config json file")
    .option("-a, --amount <number>", "Amount")
    .action(async (args) => {
        const parsedArgs = parse(args);
        if (parsedArgs) {
            const { bridgeConfig, direction } = parsedArgs;
            const providers = createProviders(direction, bridgeConfig);

            Log(LogType.Bridge, LogStatus.ToDo, `Initializing providers...`);
            await initProviders(providers);
            Log(LogType.Bridge, LogStatus.Done, `Providers initialized`);

            const sourceProvider = await providers[direction].source;
            const destinationProvider = await providers[direction].destination;

            let originAccount: { address: string; privateKey: string }, destinationAccount: { address: string; privateKey: string };
            if (args.originPrivateKey) {
                originAccount = {
                    address: sourceProvider.getAddressFromPrivateKey(args.originPrivateKey),
                    privateKey: args.originPrivateKey,
                };
            } else {
                originAccount = {
                    address: sourceProvider.getMasterAddress(),
                    privateKey: sourceProvider.getMasterPrivateKey(),
                };
            }
            if (args.destinationPrivateKey) {
                destinationAccount = {
                    address: destinationProvider.getAddressFromPrivateKey(args.destinationPrivateKey),
                    privateKey: args.destinationPrivateKey,
                };
            } else {
                destinationAccount = {
                    address: destinationProvider.getMasterAddress(),
                    privateKey: destinationProvider.getMasterPrivateKey(),
                };
            }

            Log(LogType.Bridge, LogStatus.ToDo, `Creating claim id from ${destinationAccount.address} to ${originAccount.address}...`);
            const claimId = await destinationProvider.createClaim(
                destinationAccount.privateKey,
                sourceProvider.getOtherChainAddress(originAccount.address),
            );
            Log(LogType.Bridge, LogStatus.Done, `Claim created with id ${claimId}`);

            if (bridgeConfig.isTokenBridge) {
                Log(LogType.Bridge, LogStatus.ToDo, `Approving to manage token from address ${originAccount.address} to bridge...`);
                await sourceProvider.approve(originAccount.privateKey, parsedArgs.amount);
                Log(LogType.Bridge, LogStatus.Done, `Approved successfully`);
            }

            Log(LogType.Bridge, LogStatus.ToDo, `Creating commit from ${originAccount.address} to ${destinationAccount.address}...`);
            await sourceProvider.commit(
                originAccount.privateKey,
                claimId,
                parsedArgs.amount,
                destinationProvider.getOtherChainAddress(destinationAccount.address),
            );
            Log(LogType.Bridge, LogStatus.Done, `Created commit successfully`);
            process.exit(0);
        } else {
            program.help();
            process.exit(1);
        }
    });
export default program;
